import when from '../ThirdParty/when.js';
import Cartesian2 from '../Core/Cartesian2.js';
import Cartesian3 from '../Core/Cartesian3.js';
import Color from '../Core/Color.js';
import defaultValue from '../Core/defaultValue.js';
import defined from '../Core/defined.js';
import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js";
import DistanceDisplayCondition from '../Core/DistanceDisplayCondition.js';
import GeographicTilingScheme from '../Core/GeographicTilingScheme.js';
import GeometryInstance from "../Core/GeometryInstance.js";
import PolygonGeometry from "../Core/PolygonGeometry.js";
import CesiumMath from '../Core/Math.js';
import Resource from '../Core/Resource.js';
import ClassificationType from "./ClassificationType.js";
import GroundPrimitive from './GroundPrimitive.js';
import HeightReference from './HeightReference.js';
import HorizontalOrigin from './HorizontalOrigin.js';
import LabelStyle from './LabelStyle.js';
import PerInstanceColorAppearance from './PerInstanceColorAppearance.js';
import Primitive from "./Primitive.js";
import ShadowMode from './ShadowMode.js';
import VerticalOrigin from './VerticalOrigin.js';

function EntityTileServiceProvider(options) {
    options = defaultValue(options, defaultValue.EMPTY_OBJECT);

    this._layer = options.layer;
    this._tilingScheme = new GeographicTilingScheme();
    this._tileWidth = defaultValue(options.tileWidth, 256);
    this._tileHeight = defaultValue(options.tileHeight, 256);

    this._minimumLevel = defaultValue(options.minimumLevel, 0);
    this._maximumLevel = options.maximumLevel;

    this._rectangle = defaultValue(options.rectangle, this._tilingScheme.rectangle);
    this._tileMatrixLabels = options.tileMatrixLabels;
    this._labelStyleInfo = options.labelStyleInfo;
    var deferred = when.defer();
    this._textures = {};
    this._styles = [];
    var that = this;
    if (defined(options.texureUrl)) {
        when(options.texureUrl)
            .then(function(url) {
                var textureResource = Resource.createIfNeeded(url);
                textureResource.fetchJson().then(function(data) {
                    that._textures = data;
                });
            })
            .otherwise(function(e) {
                deferred.reject(e);
            });
    }
    if (defined(options.layerDescriptionFile)) {
        when(options.layerDescriptionFile)
            .then(function(file) {
                var layerStyleResource = Resource.createIfNeeded(file);
                layerStyleResource.fetchJson().then(function(data) {
                    that._styles = data['styles'];
                });
            })
            .otherwise(function(e) {
                deferred.reject(e);
            });
    }

    var resource = Resource.createIfNeeded(options.url);
    this._resource = resource;

    this._subdomains = options.subdomains;
    if (Array.isArray(this._subdomains)) {
        this._subdomains = this._subdomains.slice();
    } else if (defined(this._subdomains) && this._subdomains.length > 0) {
        this._subdomains = this._subdomains.split('');
    } else {
        this._subdomains = ['a', 'b', 'c'];
    }

    this._usePrimitives = false;
    this._billboardCollection = options.billboardCollection;

    if(defined(this._billboardCollection)){
        this._usePrimitives = true;
    }

    this._entities = options.entities;

    this._labelLayers = options.labelLayers;

    this._modelLayers = options.modelLayers;

    this._clamped2TerrainLevel = defaultValue(options.clamped2TerrainLevel, 10);

    this._groundPrimitives = options.groundPrimitives;

    this._levelHeight = {
        '1': 17853526,
        '2': 9161454,
        '3': 5837756,
        '4': 3269316,
        '5': 1335706,
        '6': 655085,
        '7': 317509,
        '8': 111307,
        '9': 76615,
        '10': 48156,
        '11': 22561,
        '12': 9982,
        '13': 5296,
        '14': 2055,
        '15': 729,
        '16': 464,
        '17': 302,
        '18': 151
    };
}

Object.defineProperties(EntityTileServiceProvider.prototype, {
    /**
     * Gets the URL of the service hosting the imagery.
     * @memberof EntityTileServiceProvider.prototype
     * @type {String}
     * @readonly
     */
    url: {
        get: function() {
            return this._resource.url;
        }
    },

    /**
     * Gets the width of each tile, in pixels. This function should
     * not be called before {@link EntityTileServiceProvider#ready} returns true.
     * @memberof EntityTileServiceProvider.prototype
     * @type {Number}
     * @readonly
     */
    tileWidth: {
        get: function() {
            return this._tileWidth;
        }
    },

    /**
     * Gets the height of each tile, in pixels.  This function should
     * not be called before {@link EntityTileServiceProvider#ready} returns true.
     * @memberof EntityTileServiceProvider.prototype
     * @type {Number}
     * @readonly
     */
    tileHeight: {
        get: function() {
            return this._tileHeight;
        }
    },

    /**
     * Gets the maximum level-of-detail that can be requested.  This function should
     * not be called before {@link EntityTileServiceProvider#ready} returns true.
     * @memberof EntityTileServiceProvider.prototype
     * @type {Number}
     * @readonly
     */
    maximumLevel: {
        get: function() {
            return this._maximumLevel;
        }
    },

    /**
     * Gets the minimum level-of-detail that can be requested.  This function should
     * not be called before {@link EntityTileServiceProvider#ready} returns true.
     * @memberof EntityTileServiceProvider.prototype
     * @type {Number}
     * @readonly
     */
    minimumLevel: {
        get: function() {
            return this._minimumLevel;
        }
    },

    /**
     * Gets the tiling scheme used by this provider.  This function should
     * not be called before {@link EntityTileServiceProvider#ready} returns true.
     * @memberof EntityTileServiceProvider.prototype
     * @type {TilingScheme}
     * @readonly
     */
    tilingScheme: {
        get: function() {
            return this._tilingScheme;
        }
    },

    /**
     * Gets the rectangle, in radians, of the provided by this instance.  This function should
     * not be called before {@link EntityTileServiceProvider#ready} returns true.
     * @memberof EntityTileServiceProvider.prototype
     * @type {Rectangle}
     * @readonly
     */
    rectangle: {
        get: function() {
            return this._rectangle;
        }
    },

    /**
     * Gets a value indicating whether or not the provider is ready for use.
     * @memberof EntityTileServiceProvider.prototype
     * @type {Boolean}
     * @readonly
     */
    ready: {
        value: true
    },

    /**
     * Gets a promise that resolves to true when the provider is ready for use.
     * @memberof EntityTileServiceProvider.prototype
     * @type {Promise.<Boolean>}
     * @readonly
     */
    readyPromise: {
        get: function() {
            return this._readyPromise;
        }
    }
});

function requestData(provider, col, row, level, request) {
    var resource;
    var labels = provider._tileMatrixLabels;
    var tileMatrix = defined(labels) ? labels[level] : level.toString();
    var subdomains = provider._subdomains;
    var templateValues = {
        TileMatrix: tileMatrix,
        TileRow: row.toString(),
        TileCol: col.toString(),
        s: subdomains[(col + row + level) % subdomains.length]
    };

    resource = provider._resource.getDerivedResource({
        request: request
    });
    resource.setTemplateValues(templateValues);
    return resource.fetchJson();
}

EntityTileServiceProvider.prototype.requestData = function(x, y, level, request) {
    var result;

    // Couldn't load from cache
    if (!defined(result)) {
        result = requestData(this, x, y, level, request);
    }

    return result;
};

//var taskProcessor = new TaskProcessor('parseVectorTileLayer', Number.POSITIVE_INFINITY);

EntityTileServiceProvider.prototype.parseTileData = function(tile) {
    var that = this;
    that.tile = tile;
    var data = tile._data;
    var rectangle = tile._rectangle;
    var level = tile._level;
    var labels = this._tileMatrixLabels;
    var tileMatrix = defined(labels) ? labels[level] : level.toString();
    var result = [];
    var style;
    // eslint-disable-next-line no-empty
    if (defined(this._labelLayers)) {
        this._labelLayers.forEach(function(layer) {
            if (defined(data[layer]) && defined(data[layer]['features'])) {
                var type = data[layer]['type'];
                if (type === 1) {
                    var features = data[layer]['features'];
                    var len = features.length;
                    if (len > 0) {
                        var labelStyleInfo = that._labelStyleInfo.get(layer);
                        for (var i = 0; i < len; ++i) {
                            var lonIndex = Number(features[i][2][0]);
                            var latIndex = Number(features[i][2][1]);

                            var lon = CesiumMath.toDegrees(rectangle.west + rectangle.width / that._tileWidth * lonIndex);
                            var lat = CesiumMath.toDegrees(rectangle.north - rectangle.width / that._tileHeight * latIndex);

                            var id;
                            if(defined(labelStyleInfo.uniqueValue)){
                                if(Boolean(labelStyleInfo.uniqueValue)){
                                    id = features[i][1][labelStyleInfo.idIndex];
                                }else{
                                    id = tileMatrix+"_"+features[i][1][labelStyleInfo.idIndex];
                                }
                            }else{
                                id = features[i][1][labelStyleInfo.idIndex];
                            }

                            var text;
                            text = features[i][1][labelStyleInfo.textIndex];
                            if (!defined(labelStyleInfo.showLabel)) {
                                labelStyleInfo.showLabel = true;
                            }
                            if (!defined(text) && labelStyleInfo.showLabel) {
                                continue;
                            }

                            if (!defined(text) && !labelStyleInfo.showLabel) {
                                text = '';
                            }

                            style = undefined;
                            if (defined(labelStyleInfo.style)) {
                                if (labelStyleInfo.allLayerFit) {
                                    style = labelStyleInfo.style[0];
                                } else if(defined(labelStyleInfo.styleFilterIndex)){
                                    var mulitistyle = labelStyleInfo.style[tileMatrix];
                                    style= mulitistyle[features[i][1][labelStyleInfo.styleFilterIndex]];
                                }else{
                                    style = labelStyleInfo.style[tileMatrix];
                                }
                            }
                            else if (defined(labelStyleInfo.styleName)) {
                                var styleName = labelStyleInfo.styleName;
                                var stylearray = that._styles[tileMatrix];
                                var childStyle;

                                if (defined(stylearray) && defined(styleName)) {
                                    //eslint-disable-next-line no-loop-func
                                    stylearray.some(function(child) {
                                        if (child['name'] === styleName) {
                                            childStyle = child['children'];
                                            return true;
                                        }
                                    });
                                }

                                if (defined(childStyle)) {
                                    if (layer === 'POI') {
                                        var childstylename = features[i][1][4];
                                        if (defined(childstylename)) {
                                            //eslint-disable-next-line no-loop-func
                                            childStyle.some(function(child) {
                                                if (child['name'] === childstylename) {
                                                    style = child['style'][0];
                                                    return true;
                                                }
                                                return false;
                                            });
                                        }
                                    } else if (layer === '行政区划点') {
                                        //eslint-disable-next-line no-loop-func
                                        childStyle.some(function(child) {
                                            var filter = child['filter'];
                                            var index = filter.indexOf('in');
                                            if (index !== -1) {
                                                var fcodes = filter.slice(index + 5, filter.length - 1);
                                                var fcodearray = fcodes.split(',');
                                                if (fcodearray.indexOf(features[i][1][2]) !== -1) {
                                                    style = child['style'][0];
                                                    return true;
                                                }
                                            } else {
                                                index = filter.indexOf('fcode');
                                                var fcode = filter.slice(index + 9, filter.length - 1);
                                                if (fcode.indexOf(features[i][1][2]) !== -1) {
                                                    style = child['style'][0];
                                                    return true;
                                                }
                                            }
                                            return false;
                                        });
                                    }
                                }
                            }

                            var near, far, displayCondition;
                            near = 0;
                            if (that._tileWidth === 256) {
                                far = that._levelHeight[level - 1];
                                near = that._levelHeight[level];
                            } else if (that._tileWidth === 512) {
                                far = that._levelHeight[level - 2];
                                near = that._levelHeight[level-1];
                            }

                            if (defined(labelStyleInfo.alwaysvisible)) {
                                if (!labelStyleInfo.alwaysvisible) {
                                    if (that._tileWidth === 256) {
                                        near = that._levelHeight[level];
                                    } else if (that._tileWidth === 512) {
                                        near = that._levelHeight[level-1];
                                    }
                                }
                            }
                            displayCondition = new DistanceDisplayCondition(near, far);
                            var heightReference = HeightReference.NONE;
                            if (that._clamped2TerrainLevel <= Number(tileMatrix)) {
                                heightReference = HeightReference.CLAMP_TO_GROUND;
                            }

                            var label, billboardoptions;
                            billboardoptions = undefined;
                            if (defined(style)) {
                                var direction = 0;
                                var height = Number(style['graphicHeight']);
                                if (defined(style['direction'])) {
                                    direction = Number(style['direction']);
                                }
                                var pixelOffset = Cartesian2.fromArray([0, 0]);
                                if (direction === 0) {
                                    pixelOffset = Cartesian2.fromArray([Math.ceil(height / 2), 0]);
                                }
                                if (defined(style['texture'])) {
                                    billboardoptions = {
                                        image: that._textures[style['texture']],
                                        heightReference: heightReference,
                                        distanceDisplayCondition: displayCondition,
                                        width: height,
                                        height: height
                                    };
                                }
                                var showBackground = false;
                                var backgroundColor = new Color(0.165, 0.165, 0.165, 0.8);
                                if (defined(style['pointBackgroundColor']) && style['pointFillStyle'] === '#fefefe') {
                                    showBackground = true;
                                    backgroundColor = Color.fromCssColorString(style['pointBackgroundColor']);
                                }
                                label = {
                                    text: text,
                                    heightReference: heightReference,
                                    font: style['pointFillFont'],
                                    style: LabelStyle.FILL_AND_OUTLINE,
                                    fillColor: Color.fromCssColorString(style['pointFillStyle']),
                                    distanceDisplayCondition: displayCondition,
                                    showBackground: showBackground,
                                    backgroundColor: backgroundColor,
                                    outlineWidth : 3,
                                    horizontalOrigin: HorizontalOrigin.LEFT,
                                    pixelOffset: pixelOffset
                                };
                            } else {
                                label = {
                                    text: text,
                                    heightReference: heightReference,
                                    distanceDisplayCondition: displayCondition,
                                    font: '14px Microsoft YaHei',
                                    style: LabelStyle.FILL_AND_OUTLINE,
                                    verticalOrigin: VerticalOrigin.Center,
                                    outlineWidth: 4
                                };
                            }
                            if (layer === 'p_world_capital') {
                                billboardoptions = {
                                    image: that._textures['40283f8166869f51016686a06537005a'],
                                    heightReference: heightReference,
                                    distanceDisplayCondition: displayCondition,
                                    width: 12,
                                    height: 12
                                };

                                if (defined(label)) {
                                    label.horizontalOrigin = HorizontalOrigin.LEFT;
                                    label.pixelOffset = Cartesian2.fromArray([6, 0]);
                                }
                            }

                            label.show = labelStyleInfo.showLabel;

                            if (that._usePrimitives) {
                                var billoard = that._billboardCollection.add({
                                    position: Cartesian3.fromDegrees(lon, lat),
                                    image: that._textures[style['texture']],
                                    heightReference: heightReference,
                                    distanceDisplayCondition: displayCondition
                                });
                                result.push(billoard._index);
                            } else {
                                var entity = that._entities.getById(id);
                                if (!defined(entity)) {
                                    entity = that._entities.add({
                                        id: id,
                                        position: Cartesian3.fromDegrees(lon, lat),
                                        billboard: billboardoptions,
                                        label: label
                                    });
                                    entity.manualUpdate = true;
                                    entity.forceUpdate = true;
                                    result.push(entity.id);
                                } else if (defined(entity._billboard) && entity._billboard.heightReference.valueOf() !== heightReference) {
                                    entity._billboard.heightReference = heightReference;
                                } else if (defined(entity._label) && entity._label.heightReference.valueOf() !== heightReference) {
                                    entity._label.heightReference = heightReference;
                                    entity.forceUpdate = true;
                                }
                            }
                        }
                    }
                }
            }
        });
    }

    if (defined(this._modelLayers)) {
        this._modelLayers.forEach(function(layer) {
            if (defined(data['layer'][layer]) && defined(data['layer'][layer]['features'])) {
                var features = data['layer'][layer]['features'];
                var len = features.length;
                if (len > 0) {
                  var instances = [];
                    for (var i = 0; i < len; ++i) {
                        var floor = Number(features[i][1][2]);
                        if (!defined(floor) || floor === 0) {
                            floor = 1;
                        }
                        var lonlatStrArray = features[i][2][0];
                        if (Array.isArray(lonlatStrArray)) {
                            var polygonCoordinates = [];
                            for (var j = 0; j < lonlatStrArray.length; j += 2) {
                                var lonIndex = Number(lonlatStrArray[j]);
                                var latIndex = Number(lonlatStrArray[j + 1]);

                                var lon = CesiumMath.toDegrees(rectangle.west + rectangle.width / that._tileWidth * lonIndex);
                                var lat = CesiumMath.toDegrees(rectangle.north - rectangle.width / that._tileHeight * latIndex);

                                polygonCoordinates.push(lon);
                                polygonCoordinates.push(lat);
                            }

                            if (polygonCoordinates.length <= 4 || (polygonCoordinates.length === 6 && polygonCoordinates[0] === polygonCoordinates[4])) {
                                continue;
                            }

                            var id = Number(features[i][1][0]);
                            var extrudedHeightProperty = floor * 4;
                            //var entity = that._entities.getById(id);
                            // var targetHeight = floor*4;
                            // var height = 0;

                            // if(floor>20){
                            //     // eslint-disable-next-line no-loop-func
                            //     extrudedHeightProperty = new CallbackProperty(function() {
                            //         height += 0.25;
                            //         if (height > targetHeight) {
                            //             height = targetHeight;
                            //         }
                            //         return height;
                            //     }, false);
                            // }

                            // if (!defined(entity)) {
                            //     entity = that._entities.add({
                            //         id: id,
                            //         polygon: {
                            //             hierarchy: Cartesian3.fromDegreesArray(polygonCoordinates),
                            //             material: Color.WHITE,
                            //             heightReference: HeightReference.CLAMP_TO_GROUND,
                            //             // clampToGround : true,
                            //             height: 0,
                            //             // classificationType : ClassificationType.TERRAIN,
                            //             //shadows: ShadowMode.ENABLED,
                            //             // extrudedHeight:floor*4
                            //             extrudedHeight: extrudedHeightProperty
                            //         }
                            //     });
                            //     result.push(entity.id);
                            // }


                          instances.push(new GeometryInstance({
                            id : id,
                            geometry : PolygonGeometry.fromPositions({
                              extrudedHeight : extrudedHeightProperty,
                              positions : Cartesian3.fromDegreesArray(polygonCoordinates),
                              vertexFormat : PerInstanceColorAppearance.VERTEX_FORMAT
                            }),
                            attributes : {
                              color : ColorGeometryInstanceAttribute.fromColor(Color.WHITE),
                            }
                          }));
                        }
                    }
                  tile._groundprimitive = that._groundPrimitives.add(
                    new Primitive({
                      geometryInstances: instances
                      //classificationType: ClassificationType.TERRAIN,
                    })
                  );
                }

            }
        });
    }

    return result;
};

EntityTileServiceProvider.prototype.freeResources = function (tileData) {
  var that = this;
  if (defined(tileData._entityidList) && tileData._entityidList.length > 0) {
    tileData._entityidList.forEach(function (id) {
      if (that._usePrimitives) {
        var billoard = that._billboardCollection.get(id);
        that._billboardCollection.remove(billoard);
      } else {
        that._entities.removeById(id);
      }
    });
  }

  if (defined(that._groundPrimitives)&&defined(tileData._groundprimitive)){
    that._groundPrimitives.remove(tileData._groundprimitive);
  }
};

EntityTileServiceProvider.prototype.setVisible = function (visible) {
  var entitiesLength = this._entities.values.length;
  for (var i = 0; i < entitiesLength; i++) {
    var entity = this._entities.values[i];
    entity.show = visible;
  }
};

EntityTileServiceProvider.prototype.setTileDataVisibel = function(tileData) {
    var that = this;
    if (tileData._indexCollection.length > 0) {
        tileData._indexCollection.forEach(function(id) {
            var entity = that._entities.getById(id);
            entity.show = true;
        });
    }
};
export default EntityTileServiceProvider;

